
#include <rtthread.h>
#include <rtdevice.h>
#include <drv_gpio.h>

#include "modbusp.h"

const char *uart_name[] = {"uart2", "uart8"};
#define UART_NUM (sizeof(uart_name) / sizeof(uart_name[0]))
typedef struct {
    struct rt_semaphore rx_sem;
    rt_device_t dev;
    rt_timer_t timer;
    uint8_t data[1024];
    uint32_t data_idx;
    rt_base_t dir_pin;
} rs485_dev_t;
const rt_base_t dir_pins[UART_NUM] = {NU_GET_PININDEX(NU_PA, 4), NU_GET_PININDEX(NU_PA, 5)};
rs485_dev_t _rs485[UART_NUM] = {0};

static rs485_dev_t *dev_to_rs485(rt_device_t dev)
{
    for (int i = 0; i < UART_NUM; i ++)
    {
        if (dev == _rs485[i].dev && dev)
        {
            return &_rs485[i];
        }
    }
    return RT_NULL;
}

static int name_to_idx(const char* name)
{
    for (int i = 0; i < UART_NUM; i ++)
    {
        if (name && !rt_strcmp(name, uart_name[i]))
        {
            return i;
        }
    }
    return -1;
}

static rt_err_t uart_input(rt_device_t dev, rt_size_t size)
{
    char ch;
    rs485_dev_t  *rs485_dev = dev_to_rs485(dev);
    RT_ASSERT(rs485_dev);
    while (rt_device_read(dev, -1, &ch, 1) == 1)
    {
        rs485_dev->data[rs485_dev->data_idx] = ch;
        ++ rs485_dev->data_idx;
    }
    rt_tick_t timeout_tick = 50;
    rt_timer_stop(rs485_dev->timer);
    rt_timer_control(rs485_dev->timer, RT_TIMER_CTRL_SET_TIME, &timeout_tick);
    rt_timer_control(rs485_dev->timer, RT_TIMER_CTRL_SET_ONESHOT, RT_NULL);
    rt_timer_start(rs485_dev->timer);
    return RT_EOK;
}

static void timeout(void *p)
{
    static int idx = 0;
    rs485_dev_t *rs485_dev = &_rs485[(int)p];
    rt_kprintf("receive [%d] len: %d!\n", ++ idx, rs485_dev->data_idx);
    for (int i = 0; i < rs485_dev->data_idx; i ++)
    {
        rt_kprintf("%02x ", rs485_dev->data[i]);
    }
    rt_kprintf("\n");
    if (modbusp_parse(rs485_dev->data, rs485_dev->data_idx, RT_NULL))
    {
        rt_kprintf("[%d]data error!!\n", idx);
    }
    rs485_dev->data_idx = 0;
}

static void uart_thread(void *arg)
{
    rs485_dev_t *rs485_dev = &_rs485[(int)arg];
    rt_kprintf("%s test begin!", uart_name[(int)arg]);

    #define DC_REG_ENV 0
    uint8_t mdbusp_mssg_buf[256 + sizeof(modbusp_mssg_t)];
    modbusp_mssg_t *mdbusp_mssg = (modbusp_mssg_t *)mdbusp_mssg_buf;
    mdbusp_mssg->addr = 1;
    mdbusp_mssg->fc = MODBUSP_REGS_R;
    mdbusp_mssg->data[0] = DC_REG_ENV & 0xff;
    mdbusp_mssg->data[1] = (DC_REG_ENV >> 8) & 0xff;
    mdbusp_mssg->data[2] = 40;
    mdbusp_mssg->data[3] = 0;

    for(int i = 4; i < 200; i ++)
    {
        mdbusp_mssg->data[i] = i;
    }
    mdbusp_mssg->data_len = 200;
    uint8_t _modbusp_pack_buf[256 + sizeof(modbusp_mssg_t)];
    uint32_t _pack_len = 0;
    modbusp_pack(mdbusp_mssg, _modbusp_pack_buf, &_pack_len);
    RT_ASSERT(_pack_len <= sizeof(_modbusp_pack_buf));

    while(1)
    {
        rt_pin_write(rs485_dev->dir_pin, PIN_HIGH);
        rt_device_write(rs485_dev->dev, 0, _modbusp_pack_buf, _pack_len);
        rt_thread_delay(100);  // Don't forget to wait send all bytes!!!!!! //UART_WAIT_TX_EMPTY(uart_base);
        rt_pin_write(rs485_dev->dir_pin, PIN_LOW);
        rt_thread_delay(1000);
    }
}

int uart_test_init(int uart_idx, uint32_t baudrate)
{
    for (int i = 0; i < UART_NUM; i ++)
    {
        rs485_dev_t *rs485_dev = &_rs485[i];
        if (!rs485_dev->dev)
        {
            rs485_dev->dev = rt_device_find(uart_name[i]);
            RT_ASSERT(rs485_dev->dev);
            rt_sem_init(&rs485_dev->rx_sem, "rx_sem", 0, RT_IPC_FLAG_FIFO);
            struct serial_configure cfg =
            {
                baudrate, /* 115200 bits/s */
                DATA_BITS_8,      /* 8 databits */
                STOP_BITS_1,      /* 1 stopbit */
                PARITY_NONE,      /* No parity  */
                BIT_ORDER_LSB,    /* LSB first sent */
                NRZ_NORMAL,       /* Normal mode */
                RT_SERIAL_RB_BUFSZ, /* Buffer size */
                RT_SERIAL_FLOWCONTROL_NONE, /* Off flowcontrol */
                0
            };
            rt_device_control(rs485_dev->dev, RT_DEVICE_CTRL_CONFIG, &cfg);
            rt_device_open(rs485_dev->dev, RT_DEVICE_FLAG_INT_RX);
            rt_device_set_rx_indicate(rs485_dev->dev, uart_input);
            rs485_dev->timer = rt_timer_create("timer", timeout,
                                    (void *)i,  50, RT_TIMER_FLAG_SOFT_TIMER);
            RT_ASSERT(rs485_dev->timer);
            rt_memset(rs485_dev->data, 0, sizeof(rs485_dev->data));
            rs485_dev->data_idx = 0;
            rs485_dev->dir_pin = dir_pins[i];
            rt_pin_mode(rs485_dev->dir_pin, PIN_MODE_OUTPUT);
            rt_pin_write(rs485_dev->dir_pin, PIN_LOW);
            rt_thread_delay(100);
        }
    }

    rt_thread_t  tid = rt_thread_create("uart_test", uart_thread, (void *)uart_idx, 1024, 12, 10);
    RT_ASSERT(tid);
    rt_thread_startup(tid);
    return 0;
}

static int uart_test(int argc, char *argv[])
{
    rt_err_t ret = RT_EOK;
    int idx = argc > 2? name_to_idx(argv[1]) : -1;
    uint32_t baud = argc > 2? atoi(argv[2]) : 0;
    if (idx >= 0 && baud >= 1200 && baud <= 10*1000*1000)
    {
        uart_test_init(idx, baud);
    }
    return ret;
}
MSH_CMD_EXPORT(uart_test, uart device sample test);
